WebWork has one of the most advanced type conversion abilities in any web-based framework in any Java language. Generally, you don't need to do anything to take advantage of it, other than name your HTML inputs (form elements and other GET/POST parameters) names that are valid OGNL expressions. A Simple ExampleType conversion is great for situations where you need to turn a String in to a more complex object. Because the web is type-agnostic (everything is a string in HTTP), WebWork's type conversion features are very useful. For instance, if you were prompting a user to enter in coordinates in the form of a string (such as "3, 22"), you could have WebWork do the conversion both from String to Point and from Point to String. Using this "point" example, if your action (or another compound object in which you are setting properties on) has a corresponding ClassName-conversion.properties file, WebWork will use the configured type converters for conversion to and from strings. So turning "3, 22" in to new Point(3, 22) is done by merely adding the following entry to ClassName-conversion.properties (Note that the PointConverter should impl the ognl.TypeConverter interface): point = com.acme.PointConverter Your type converter should be sure to check what class type it is being requested to convert. Because it is used for both to and from strings, you will need to split the conversion method in to two parts: one that turns Strings in to Points, and one that turns Points in to Strings. After this is done, you can now reference your point (using <ww:property value="post"/> in JSP or ${point} in FreeMarker) and it will be printed as "3, 22" again. As such, if you submit this back to an action, it will be converted back to a Point once again. In some situations you may wish to apply a type converter globally. This can be done by editing the file xwork-conversion.properties in the root of your class path (typically WEB-INF/classes) and providing a property in the form of the class name of the object you wish to convert on the left hand side and the class name of the type converter on the right hand side. For example, providing a type converter for all Point objects would mean adding the following entry: com.acme.Point = com.acme.PointConverter
WebWork ships with a helper base class that makes converting to and from Strings very easy. The class is com.opensymphony.webwork.util.WebWorkTypeConverter. This class makes it very easy for you to write type converters that handle converting objects to Strings as well as from Strings. From the JavaDocs for this class: Base class for type converters used in WebWork. This class provides two abstract methods that are used to convert both to and from strings – the critical functionality that is core to WebWork's type coversion system. Type converters do not have to use this class. It is merely a helper base class, although it is recommended that you use this class as it provides the common type conversion contract required for all web-based type conversion. There's a hook (fall back method) calledperformFallbackConversion of which
could be used to perform some fallback conversion if convertValue method of this
failed. By default it just ask its super class (Ognl's DefaultTypeConverter) to do the conversion.
To allow WebWork to recongnize that a converison error has occurred, throw an XWorkException or
preferable a TypeConversionException.
Built in Type Conversion SupportXWork will automatically handle the most common type conversion for you. This includes support for converting to and from Strings for each of the following:
Relationship to Parameter NamesThe best way to take advantage of WebWork's type conversion is to utilize complete objects (ideally your domain objects directly), rather than submitting form values on to intermediate primitives and strings in your action and then converting those values to full objects in the execute() method. Some tips for achieving this are:
Creating a Type ConverterTo create a type converter one would need to extends WebWorkTypeConverter. public class MyConverter extends WebWorkTypeConverter { public Object convertFromString(Map context, String[] values, Class toClass) { ..... } public String convertToString(Map context, Object o) { ..... } }
Advanced Type ConversionWebWork also has some very advanced, yet easy-to-use, type conversion features. Null property handling will automatically create objects where null references are found. Collection and map support provides intelligent null handling and type conversion for Java Collections. Type conversion error handling provides an easy way to distinguish the difference between an input validation problem from an input type conversion problem. Null Property HandlingProvided that the key #CREATE_NULL_OBJECTS is in the action context with a value of true (this key is set only during the execution of the com.opensymphony.xwork.interceptor.ParametersInterceptor), OGNL expressions that have caused a NullPointerException will be temporarily stopped for evaluation while the system automatically tries to solve the null references by automatically creating the object. The following rules are used when handling null references:
For example, if a form element has a text field named person.name and the expression person evaluates to null, then this class will be invoked. Because the person expression evaluates to a Person class, a new Person is created and assigned to the null reference. Finally, the name is set on that object and the overall effect is that the system automatically created a Person object for you, set it by calling setPerson() and then finally called getPerson().setName() as you would typically expect.
Collection and Map SupportWebWork supports ways to determine the object type found in collections. This is done via an ObjectTypeDeterminer. The default implementation is provided. The JavaDocs explain how map and colelction support is determined in the DefaultObjectTypeDeterminer: This ObjectTypeDeterminer looks at the Class-conversion.properties for entries that indicated what objects are contained within Maps and Collections. For Collections, such as Lists, the element is specified using the pattern Element_xxx, where xxx is the field name of the collection property in your action or object. For Maps, both the key and the value may be specified by using the pattern Key_xxx and Element_xxx, respectively. From WebWork 2.1.x, the Collection_xxx format is still supported and honored, although it is deprecated and will be removed eventually.
Additionally, you can create your own custom ObjectTypeDeterminer by implementing the ObjectTypeDeterminer interface. There is also an optional ObjectTypeDeterminer that utilizes Java 5 generics. See the J2SE 5 Support page for more information. Indexing a collection by a property of that collectionIt is also possible using webwork to get a unique element of a collection, by passing the value of a given property of that element. By default, the property of the element of the collection is determined in Class-conversion.properties using KeyProperty_xxx=yyy where xxx is the property of the bean 'Class' that returns the collection and yyy is the property of the collection element that we want to index on. Here is an example with the following two classes: MyAction.java /** * @return a Collection of Foo objects */ public Collection getFooCollection() { return foo; }
Foo.java /** * @return a unique identifier */ public Long getId() { return id; }
Then put KeyProperty_fooCollection=id in my MyAction-conversion.properties file. This would allow to the use of fooCollection(someIdValue) to get the Foo object with value someIdValue in the Set fooCollection. For example, fooCollection(22) would return the Foo object in the fooCollection collection whose id property value was 22. This is useful, because it ties a collection element directly to its unique identifier and therefore does not force you to use an index and thus allows you to edit the elements of a collection associated to a bean without any additional code. For example, parameter name fooCollection(22).name and value Phil would set name the Foo object in the fooCollection collection whose id property value was 22 to be Phil. Webwork automatically converts the type of the parameter sent in to the type of the key property using type conversion. Unlike Map and List element properties, if fooCollection(22) does not exist it will not be created. To do that, use the notation fooCollection.makeNew[index] where index is an integer 0, 1, and so on. Thus, parameter value pairs fooCollection.makeNew[0]=Phil and fooCollection.makeNew[1]=John would add two new Foo objects to fooCollection one with name property value Phil and the other with name property value Bar. Note, however, that in the case of a Set, the equals and hashCode methods should be defined such that they don't only include the id property. That will cause one element of the null id propertied Foos to be removed from the Set. An advanced example for indexed Lists and MapsHere is the model bean used within the list. MyBean.java public class MyBean implements Serializable { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "MyBean{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
The action has a beanList attribute initialized with an empty ArrayList. MyBeanAction.java ublic class MyBeanAction implements Action { private List beanList = new ArrayList(); private Map beanMap = new HashMap(); public List getBeanList() { return beanList; } public void setBeanList(List beanList) { this.beanList = beanList; } public Map getBeanMap() { return beanMap; } public void setBeanMap(Map beanMap) { this.beanMap = beanMap; } public String execute() throws Exception { return SUCCESS; } }
These conversion.properties tell the TypeConverter to use MyBean instances as elements of the List. MyBeanAction-conversion.properties KeyProperty_beanList=id
Element_beanList=MyBean
CreateIfNull_beanList=true
When submitting this via a form, the (id) value is used as KeyProperty for the MyBean instances in the beanList. MyBeanAction.jsp <ww:iterator value="beanList" id="bean"> <ww:textfield name="beanList(%{bean.id}).name" /> </ww:iterator>
Java 5 EnumerationWhat we talk aboutOne of Java 5's improvements is providing enumeration facility. public static final int SEASON_WINTER = 0; public static final int SEASON_SPRING = 1; public static final int SEASON_SUMMER = 2; public static final int SEASON_FALL = 3;
Java 5.0 now provides the following construct: public static enum Season { WINTER, SPRING, SUMMER, FALL };
Implementing Java 5 Enumeration Type Conversion1. myAction-conversion.properties
2. myAction.java
3. EnumTypeConverter EnumTypeConverter.java /** * EnumTypeConverter * This class converts java 5 enums to String and from String[] to enum. * @author Tamara Cattivelli * @version 1.0 * @since <pre>01.06.2006</pre> */ import ognl.DefaultTypeConverter; import java.util.Map; public class EnumTypeConverter extends DefaultTypeConverter { /** * Converts the given object to a given type. How this is to be done is * implemented in toClass. * The OGNL context, o and toClass are given. * This method should be able to handle conversion in general without * any context or object specified. * @param context - OGNL context under which the conversion is being done * @param o - the object to be converted * @param toClass - the class that contains the code to convert to enumeration * @return Converted value of type declared in toClass or * TypeConverter.NoConversionPossible to indicate that the conversion was not possible. */ public Object convertValue(Map context, Object o, Class toClass) { if (o instanceof String[]) { return convertFromString(((String[]) o)[0], toClass); } else if (o instanceof String) { return convertFromString((String) o, toClass); } return super.convertValue(context, o, toClass); } /** * Converts one or more String values to the specified class. * @param value - the String values to be converted, such as those submitted from an HTML form * @param toClass - the class to convert to * @return the converted object * @see com.opensymphony.webwork.util.WebWorkTypeConverter#convertFromString(java.util.Map, String[], Class) */ public Object convertFromString(String value, Class toClass) { return Enum.valueOf(toClass, value); } }
4. JSP In your jsp you can access an enumeration value just normal by using the known <ww:property>-Tag: <ww:property value="myField"/>
For any further questions contact me, Tamara Cattivelli, at [[email protected]] Type Conversion Error HandlingAny error that occurs during type conversion may or may not wish to be reported. For example, reporting that the input "abc" could not be converted to a number might be important. On the other hand, reporting that an empty string, "", cannot be converted to a number might not be important - especially in a web environment where it is hard to distinguish between a user not entering a value vs. entering a blank value. By default, all conversion errors are reported using the generic i18n key xwork.default.invalid.fieldvalue, which you can override (the default text is Invalid field value for field "xxx", where xxx is the field name) in your global i18n resource bundle. However, sometimes you may wish to override this message on a per-field basis. You can do this by adding an i18n key associated with just your action (Action.properties) using the pattern invalid.fieldvalue.xxx, where xxx is the field name. It is important to know that none of these errors are actually reported directly. Rather, they are added to a map called conversionErrors in the ActionContext. There are several ways this map can then be accessed and the errors can be reported accordingly.
There are two ways the error reporting can occur:
By default, the conversion interceptor is included in webwork-default.xml in the default stack, so if you don't want conversion errors reporting globally, you'll need to change the interceptor stack and add additional validation rules. |